# -*- coding: utf-8 -*-
# @Time    : 2024/7/1 14:54
# @Author  : yujiahao
# @File    : 10_numpy_sort_search.py
# @description:NumPy排序和搜索功能

"""
NumPy 提供了多种排序函数，这些排序函数可以实现不同的排序算法。

排序算法特征主要体现在以下四个方面：执行速度，最坏情况下的复杂度，所需的工作空间以及算法的稳定性。

下表列举了三种排序算法：

NumPy排序算法
种类    速度    最坏复杂度         工作空间    稳定性
quicksort（快速排序）  1    O(n^2)        0        不稳定
mergesort（归并排序）  2    O(n * log(n))  ~n/2     稳定
heapsort（堆排序）    3    O(n * log(n))  0        不稳定

1. 快速排序（quicksort）
   特点：
   - 速度：通常是最快的排序算法之一，平均时间复杂度为 O(n log n)。
   - 最坏复杂度：在最坏情况下，时间复杂度为 O(n^2)，例如，当输入数组已经有序时。
   - 工作空间：原地排序算法，所需的工作空间为 O(1)。
   - 稳定性：不稳定，即相同元素的相对顺序可能会改变。
   适用场景：适用于大多数情况，特别是当输入数据是随机分布时。

2. 归并排序（mergesort）
   特点：
   - 速度：速度较快，时间复杂度为 O(n log n)。
   - 最坏复杂度：在最坏情况下，时间复杂度仍为 O(n log n)，具有很好的性能保证。
   - 工作空间：需要额外的工作空间，约为 O(n/2)。
   - 稳定性：稳定，即相同元素的相对顺序不会改变。
   适用场景：适用于需要稳定排序的情况，特别是当数据较大且需要保证最坏情况下的性能时。

3. 堆排序（heapsort）
   特点：
   - 速度：速度较快，时间复杂度为 O(n log n)。
   - 最坏复杂度：在最坏情况下，时间复杂度仍为 O(n log n)。
   - 工作空间：原地排序算法，所需的工作空间为 O(1)。
   - 稳定性：不稳定，即相同元素的相对顺序可能会改变。
   适用场景：适用于需要原地排序且不需要稳定性的情况。

NumPy 中的排序函数
NumPy 提供了 numpy.sort() 和 numpy.argsort() 函数，这些函数可以通过参数指定使用不同的排序算法。

numpy.sort(a, axis=-1, kind=None, order=None)
- a：要排序的数组。
- axis：沿着哪个轴排序，默认是最后一个轴。
- kind：指定排序算法，可以是 'quicksort'、'mergesort' 或 'heapsort'。
- order：如果数组包含字段，则是要排序的字段名。

numpy.argsort(a, axis=-1, kind=None, order=None)
- a：要排序的数组。
- axis：沿着哪个轴排序，默认是最后一个轴。
- kind：指定排序算法，可以是 'quicksort'、'mergesort' 或 'heapsort'。
- order：如果数组包含字段，则是要排序的字段名。


总结：
- 快速排序：通常最快，但在最坏情况下性能较差，不稳定。
- 归并排序：稳定，性能稳定，但需要额外的工作空间。
- 堆排序：性能稳定，不需要额外的工作空间，但不稳定。


"""
import numpy as np


# todo 一、排序

def numpy_sort():
    # todo 1.1、numpy.sort()

    '''
    numpy.sort() 对输入数组执行排序，并返回一个数组副本。它具有以下参数：
    numpy.sort(a, axis, kind, order)

    参数说明：
        a：要排序的数组；
        axis：沿着指定轴进行排序，如果没有指定 axis，默认在最后一个轴上排序，若 axis=0 表示按列排序，axis=1 表示按行排序；
        kind：默认为 quicksort（快速排序）；
        order：若数组设置了字段，则 order 表示要排序的字段。
    '''

    a = np.array([[3, 7], [9, 1]])
    print('a数组是：')
    print(a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 调用sort()函数
    print(np.sort(a))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 按列排序：
    print('按列排序', np.sort(a, axis=0))
    # 设置在sort函数中排序字段
    dt = np.dtype([('name', 'S10'), ('age', int)])
    a = np.array([("raju", 21), ("anil", 25), ("ravi", 17), ("amar", 27)], dtype=dt)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 再次打印a数组
    print('再次打印a数组\n', a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 按name字段排序
    print(np.sort(a, order='name'))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 1.2、numpy.argsort()

    # argsort() 沿着指定的轴，对输入数组的元素值进行排序，并返回排序后的元素索引数组。示例如下：
    a = np.array([90, 29, 89, 12])
    print("原数组\n", a)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    sort_ind = np.argsort(a)
    print("打印排序元素索引值\n", sort_ind)
    # 使用索引数组对原数组排序，表示第一最小在索引3处，第二最小在索引 1处，以此类推
    sort_a = a[sort_ind]

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    print("打印排序数组\n")
    for i in sort_ind:
        print(a[i], end=" ")

    # todo 1.3、numpy.lexsort()

    '''
    numpy.argsort()：对单个数组进行排序，返回的是排序后的索引数组。
    numpy.lexsort()：对多个键（数组）进行排序，返回的是根据多个键按字典顺序排序后的索引数组。
    '''

    # numpy.lexsort() 按键序列对数组进行排序，它返回一个已排序的索引数组，类似于 numpy.argsort()。

    a = np.array(['a', 'b', 'c', 'd', 'e'])
    b = np.array([12, 90, 380, 12, 211])
    ind = np.lexsort((a, b))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 打印排序元素的索引数组
    print(ind)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 使用索引数组对数组进行排序
    for i in ind:
        print(a[i], b[i])


# todo 二、查找
def numpy_search():
    # todo 2.1 numpy.nonzero()
    #  该函数从数组中查找非零元素的索引位置
    b = np.array([12, 90, 380, 12, 211])
    print("原数组b", b)
    print("打印非0元素的索引位置")
    print(b.nonzero())

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2.2 numpy.where()
    '''numpy.where() 的返回值是满足了给定条件的元素索引值。'''

    b = np.array([12, 90, 380, 12, 211])
    print(np.where(b > 12))
    c = np.array([[20, 24], [21, 23]])
    print(np.where(c > 20))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2.3 numpy.extract()
    '''该函数的返回值是满足了给定条件的元素值，'''
    x = np.arange(9.).reshape(3, 3)
    print(x)
    # 设置条件选择偶数元素
    # mod 用于计算数组元素的模（取余数）
    condition = np.mod(x, 2) == 0
    # 输出布尔值数组
    print(condition)
    # 按condition提取满足条件的元素值
    print(np.extract(condition, x))

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2.4 numpy.argmax()
    '''该函数返回最大值的的索引'''
    a = np.array([[30, 40, 70], [80, 20, 10], [50, 90, 60]])
    # a数组
    print(a)
    # argmax() 函数，返回最大值索引
    print(np.argmax(a))
    # 将数组以一维展开
    print(a.flatten())
    # 沿轴 0 的最大值索引：
    maxindex = np.argmax(a, axis=0)
    print(maxindex)
    # 沿轴 1 的最大值索引
    maxindex = np.argmax(a, axis=1)
    print(maxindex)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # todo 2.5 numpy.argmin()
    '''argmin() 求最小值索引。'''
    b = np.array([[3, 4, 7], [8, 2, 1], [5, 9, 6]])
    print('数组b：')
    print(b)
    # 调用 argmin()函数，返回最小值索引
    minindex = np.argmin(b)
    print(minindex)

    print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

    # 展开数组中的最小值：
    print(b.flatten()[minindex])
    # 沿轴 0 的最小值索引：
    minindex = np.argmin(b, axis=0)
    print(minindex)
    # 沿轴 1 的最小值索引：
    minindex = np.argmin(b, axis=1)
    print(minindex)


def main():
    # numpy_sort()
    numpy_search()


if __name__ == '__main__':
    main()
